﻿Public Class frmBrowser

    Private ConsoleProcs As New PurpleKnife.Library.ProcessManager
    Private Network As New PurpleKnife.Library.pkGroup
    Private RootNode As TreeNode
    Private CfgFileName As String = ""

    Private Enum TreeViewImages
        Network = 0
        Switch_Unknown = 1
        Group = 2
    End Enum

    Private Sub frmBrowser_Load(sender As Object, e As System.EventArgs) Handles Me.Load
        Network.DisplayName = "My Network"
        LoadFile(CfgFileName)
        BuildTreeView()
    End Sub

    Private Sub LoadFile(FileName As String)
        'Dim NewGroup As PurpleKnife.Library.pkGroup
        'Dim NewSwitch1 As PurpleKnife.Library.pkSwitch
        'Dim NewSwitch2 As PurpleKnife.Library.pkSwitch
        'NewGroup = New PurpleKnife.Library.pkGroup
        'NewGroup.DisplayName = "Cloud Core"
        'Network.Members.Add(NewGroup)
        'NewSwitch1 = New PurpleKnife.Library.pkSwitch
        'NewSwitch1.DisplayName = "VMX-North"
        'NewGroup.Members.Add(NewSwitch1)
        'NewSwitch2 = New PurpleKnife.Library.pkSwitch
        'NewSwitch2.DisplayName = "VMX-South"
        'NewGroup.Members.Add(NewSwitch2)
        ' TODO: Obtain last cfg file name from registry
        If Not FileName = "" Then
            Try
                Dim cfg As New PurpleKnife.Library.Configuration
                cfg.Load(FileName)
                Network = cfg.RootGroup
                CfgFileName = FileName
            Catch ex As Exception
                MsgBox("Unable to load configuration: " & ex.Message)
            End Try
        End If
    End Sub

    Private Sub SaveFile(Optional FileName As String = "")
        Dim saveDlg As New SaveFileDialog
        Dim saveDlgRes As DialogResult
        Dim saveFileName As String
        If FileName = "" Then
            With saveDlg
                .AddExtension = True
                .AutoUpgradeEnabled = True
                .CheckPathExists = True
                .DefaultExt = "cfg"
                .DereferenceLinks = True
                .Filter = "Configuration Files|*.cfg|All Files|*.*"
                .OverwritePrompt = True
                .RestoreDirectory = True
                .Title = "Save Configuration File"
                .ValidateNames = True
                saveDlgRes = .ShowDialog(Me)
                saveFileName = .FileName
            End With
            If saveDlgRes = Windows.Forms.DialogResult.Cancel Then Exit Sub
        Else
            saveFileName = FileName
        End If
        Try
            Dim persist As New PurpleKnife.Library.Configuration
            persist.ConfigFilePath = saveFileName
            persist.RootGroup = Network
            persist.Save()
            CfgFileName = saveFileName
        Catch ex As Exception
            MsgBox("Unable to save configuration: " & ex.Message, vbExclamation Or vbOKOnly, "PurpleKnife")
        End Try
    End Sub


    Private Sub BuildTreeView(Optional SelectedNode As String = "")
        trvNetwork.Nodes.Clear()
        RootNode = GetRootNode()
        trvNetwork.Nodes.Add(RootNode)
        BuildSubTree(RootNode, Network, SelectedNode)
    End Sub

    Private Sub BuildSubTree(TreeParent As TreeNode, ElementParent As PurpleKnife.Library.pkGroup, Optional SelectedNode As String = "")
        Dim NewTreeNode As TreeNode
        Dim NewTreeNodeInfo As BrowserNodeInfo
        Dim NewGroup As PurpleKnife.Library.pkGroup
        Dim NewSwitch As PurpleKnife.Library.pkSwitch
        For Each Element As PurpleKnife.Library.pkTreeElement In ElementParent.Members
            If TypeOf (Element) Is PurpleKnife.Library.pkGroup Then
                NewGroup = DirectCast(Element, PurpleKnife.Library.pkGroup)
                NewTreeNodeInfo = New BrowserNodeInfo
                NewTreeNodeInfo.ListElement = NewGroup
                NewTreeNode = New TreeNode
                NewTreeNode.Text = Element.DisplayName
                NewTreeNode.ImageIndex = TreeViewImages.Group
                NewTreeNode.SelectedImageIndex = NewTreeNode.ImageIndex
                NewTreeNode.Tag = NewTreeNodeInfo
                TreeParent.Nodes.Add(NewTreeNode)
                NewTreeNode.EnsureVisible()
                If SelectedNode.ToLower = NewTreeNode.Text.ToLower Then trvNetwork.SelectedNode = NewTreeNode
                BuildSubTree(NewTreeNode, NewGroup, SelectedNode)
            ElseIf TypeOf (Element) Is PurpleKnife.Library.pkSwitch Then
                NewSwitch = DirectCast(Element, PurpleKnife.Library.pkSwitch)
                NewTreeNodeInfo = New BrowserNodeInfo
                NewTreeNodeInfo.ListElement = NewSwitch
                NewTreeNode = New TreeNode
                NewTreeNode.Text = Element.DisplayName
                NewTreeNode.ImageIndex = TreeViewImages.Switch_Unknown
                NewTreeNode.SelectedImageIndex = NewTreeNode.ImageIndex
                NewTreeNode.Tag = NewTreeNodeInfo
                TreeParent.Nodes.Add(NewTreeNode)
                NewTreeNode.EnsureVisible()
                If SelectedNode.ToLower = NewTreeNode.Text.ToLower Then trvNetwork.SelectedNode = NewTreeNode
            End If
        Next
    End Sub

    Private Function GetRootNode() As TreeNode
        Dim Root As New TreeNode
        Dim RootInfo As New BrowserNodeInfo
        Root.Text = IIf(Network.DisplayName = "", "My Network", Network.DisplayName).ToString
        Root.ToolTipText = "Network Root"
        Root.ImageIndex = 0
        RootInfo.IsRoot = True
        RootInfo.ListElement = New PurpleKnife.Library.pkGroup
        Root.Tag = RootInfo
        Return Root
    End Function

    Private Sub trvNetwork_NodeMouseClick(sender As Object, e As System.Windows.Forms.TreeNodeMouseClickEventArgs) Handles trvNetwork.NodeMouseClick
        Dim Element As TreeNode = e.Node
        Dim ElementInfo As BrowserNodeInfo = DirectCast(Element.Tag, BrowserNodeInfo)
        If ElementInfo.IsRoot Then
            ' Root Item
            MenuNetwork.Visible = True
            MenuGroup.Visible = False
            MenuSwitch.Visible = False
            If e.Button = Windows.Forms.MouseButtons.Right Then
                trvNetwork.SelectedNode = Element
                ContextMenuNetwork.Show(DirectCast(sender, Control), e.Location, ToolStripDropDownDirection.BelowRight)
            End If
        ElseIf TypeOf (ElementInfo.ListElement) Is PurpleKnife.Library.pkGroup Then
            ' Group Item
            MenuNetwork.Visible = False
            MenuGroup.Visible = True
            MenuSwitch.Visible = False
            If e.Button = Windows.Forms.MouseButtons.Right Then
                trvNetwork.SelectedNode = Element
                ContextMenuGroup.Show(DirectCast(sender, Control), e.Location, ToolStripDropDownDirection.BelowRight)
            End If
        ElseIf TypeOf (ElementInfo.ListElement) Is PurpleKnife.Library.pkSwitch Then
            ' Switch Item
            MenuNetwork.Visible = False
            MenuGroup.Visible = False
            MenuSwitch.Visible = True
            If e.Button = Windows.Forms.MouseButtons.Right Then
                trvNetwork.SelectedNode = Element
                ContextMenuSwitch.Show(DirectCast(sender, Control), e.Location, ToolStripDropDownDirection.BelowRight)
            End If
        End If
    End Sub

    ' recursively build list of all subgroups of given group
    Private Function GetSubGroups(Group As PurpleKnife.Library.pkGroup) As List(Of PurpleKnife.Library.pkGroup)
        Dim GroupList As New List(Of PurpleKnife.Library.pkGroup)
        Dim GroupItem As PurpleKnife.Library.pkGroup
        For Each Item As PurpleKnife.Library.pkTreeElement In Group.Members
            If TypeOf (Item) Is PurpleKnife.Library.pkGroup Then
                GroupItem = DirectCast(Item, PurpleKnife.Library.pkGroup)
                GroupList.Add(GroupItem)
                GroupList.AddRange(GetSubGroups(GroupItem))
            End If
        Next
        Return GroupList
    End Function

    Private Function GetGroupByName(RootGroup As PurpleKnife.Library.pkGroup, GroupName As String) As PurpleKnife.Library.pkGroup
        Dim SubGrp As PurpleKnife.Library.pkGroup
        If RootGroup.DisplayName.ToLower = GroupName.ToLower Then
            Return RootGroup
        Else
            For Each Item As PurpleKnife.Library.pkTreeElement In RootGroup.Members
                If TypeOf (Item) Is PurpleKnife.Library.pkGroup Then
                    If Item.DisplayName.ToLower = GroupName.ToLower Then
                        Return DirectCast(Item, PurpleKnife.Library.pkGroup)
                    Else
                        SubGrp = GetGroupByName(DirectCast(Item, PurpleKnife.Library.pkGroup), GroupName)
                        If Not SubGrp Is Nothing Then
                            Return SubGrp
                        End If
                    End If
                End If
            Next
        End If
        Return Nothing
    End Function

    Public Function NameExists(Name As String, Optional RootGroup As PurpleKnife.Library.pkGroup = Nothing) As Boolean
        If RootGroup Is Nothing Then RootGroup = Network
        Dim SubGrp As PurpleKnife.Library.pkGroup
        Dim Exists As Boolean = False
        If RootGroup.DisplayName.ToLower = Name.ToLower Then Return True
        For Each Item As PurpleKnife.Library.pkTreeElement In RootGroup.Members
            If Item.DisplayName.ToLower = Name.ToLower Then Return True
            If TypeOf (Item) Is PurpleKnife.Library.pkGroup Then
                SubGrp = DirectCast(Item, PurpleKnife.Library.pkGroup)
                Exists = NameExists(Name, SubGrp)
                If Exists Then Return True
            End If
        Next
        Return False
    End Function

    ' add group list to given combo box and select given item
    Private Sub AddGroupList(GroupList As List(Of PurpleKnife.Library.pkGroup), ComboBox As System.Windows.Forms.ComboBox, Optional SelectedItem As String = "")
        ' add given groups to parent list
        For Each Item As PurpleKnife.Library.pkGroup In GroupList
            ComboBox.Items.Add(Item.DisplayName)
        Next
        ' select given group as default
        If Not SelectedItem = "" Then ComboBox.SelectedIndex = ComboBox.Items.IndexOf(SelectedItem)
    End Sub

    Private Sub ShowAsDialog(DialogForm As System.Windows.Forms.Form)
        DialogForm.Top = Me.Top + 40
        DialogForm.Left = Me.Left + 20
        DialogForm.ShowDialog(Me)
    End Sub



    Private Sub AddSwitch()

        ' set defaults
        With frmNetAddSwitch
            .cmbParentGroup.Items.Clear()
            .txtDisplayName.Clear()
            .txtIPAddressHostname.Clear()
            .txtPassword.Clear()
            .txtPort.Text = "22"
            .txtUserName.Clear()
        End With

        ' create parent group dropdown list
        ' add root group to parent list
        frmNetAddSwitch.cmbParentGroup.Items.Add(Network.DisplayName)
        ' add all groups to parent list
        Dim GroupList As List(Of PurpleKnife.Library.pkGroup) = GetSubGroups(Network)
        AddGroupList(GroupList, frmNetAddSwitch.cmbParentGroup, trvNetwork.SelectedNode.Text)

        ' default cancel
        frmNetAddSwitch.Result = MsgBoxResult.Cancel

        ' show the dialog
        ShowAsDialog(frmNetAddSwitch)

        ' ignore if cancel
        If frmNetAddSwitch.Result = MsgBoxResult.Ok Then

            ' get parent group
            Dim ParentGrp As PurpleKnife.Library.pkGroup = GetGroupByName(Network, frmNetAddSwitch.cmbParentGroup.Text)

            ' create new switch object
            Dim NewSwitch As New PurpleKnife.Library.pkSwitch
            With NewSwitch
                .DisplayName = frmNetAddSwitch.txtDisplayName.Text
                .SSHUserName = frmNetAddSwitch.txtUserName.Text
                .SSHPassword = frmNetAddSwitch.txtPassword.Text
                .SSHHost = frmNetAddSwitch.txtIPAddressHostname.Text
                .SSHPort = Integer.Parse(frmNetAddSwitch.txtPort.Text)
            End With

            ' add new switch object to parent group
            ParentGrp.Members.Add(NewSwitch)

            ' rebuild treeview
            BuildTreeView(NewSwitch.DisplayName)

        End If

    End Sub

    Private Sub AddGroup()
        With frmNetAddGroup
            .cmbParentGroup.Items.Clear()
            .txtDisplayName.Clear()
        End With

        ' create parent group dropdown list
        ' add root group to parent list
        frmNetAddGroup.cmbParentGroup.Items.Add(Network.DisplayName)
        ' add all groups to parent list
        Dim GroupList As List(Of PurpleKnife.Library.pkGroup) = GetSubGroups(Network)
        AddGroupList(GroupList, frmNetAddGroup.cmbParentGroup, trvNetwork.SelectedNode.Text)

        frmNetAddGroup.Result = MsgBoxResult.Cancel

        ShowAsDialog(frmNetAddGroup)

        ' ignore if cancel
        If frmNetAddGroup.Result = MsgBoxResult.Ok Then

            ' get parent group
            Dim ParentGrp As PurpleKnife.Library.pkGroup = GetGroupByName(Network, frmNetAddGroup.cmbParentGroup.Text)

            ' create new switch object
            Dim NewGroup As New PurpleKnife.Library.pkGroup
            With NewGroup
                .DisplayName = frmNetAddGroup.txtDisplayName.Text
            End With

            ' add new switch object to parent group
            ParentGrp.Members.Add(NewGroup)

            ' rebuild treeview
            BuildTreeView(NewGroup.DisplayName)

        End If
    End Sub

    Private Sub OpenConsole(Switch As PurpleKnife.Library.pkSwitch)
        ' Prepare ProcessStartInfo and set environment variables
        Dim StartInfo As New ProcessStartInfo
        With StartInfo
            .FileName = "PurpleKnife.Shell.exe"
            .UseShellExecute = False
            .EnvironmentVariables("PK_SWITCH_DISPLAYNAME") = Switch.DisplayName
            .EnvironmentVariables("PK_IPC_CHANNEL") = "test"
            .EnvironmentVariables("PK_SSH_ADDR") = Switch.SSHHost
            .EnvironmentVariables("PK_SSH_PORT") = Switch.SSHPort.ToString
            .EnvironmentVariables("PK_SSH_USER") = Switch.SSHUserName
            .EnvironmentVariables("PK_SSH_PASS") = Switch.SSHPassword
            .EnvironmentVariables("PK_SSH_KEY_FINGERPRINT") = Switch.SSHKeyFingerprint
            .EnvironmentVariables("PK_PARENT_PID") = Process.GetCurrentProcess.Id.ToString
        End With
        Try
            ' start PurpleKnife.Shell
            ConsoleProcs.Start(StartInfo)
        Catch ex As Exception
            MsgBox("Couldn't start Console: " & ex.Message, vbExclamation Or vbOKOnly, "PurpleKnife")
        End Try
    End Sub


    Private Sub HelpNistonCloud_Click(sender As System.Object, e As System.EventArgs) Handles HelpNistonCloud.Click
        frmAbout.ShowDialog(Me)
    End Sub

    Private Sub NetworkAddSwitch_Click(sender As System.Object, e As System.EventArgs) Handles NetworkAddSwitch.Click
        Call AddSwitch()
    End Sub

    Private Sub ContextNetworkAddSwitch_Click(sender As System.Object, e As System.EventArgs) Handles ContextNetworkAddSwitch.Click
        Call AddSwitch()
    End Sub

    Private Sub NetworkAddGroup_Click(sender As System.Object, e As System.EventArgs) Handles NetworkAddGroup.Click
        Call AddGroup()
    End Sub

    Private Sub ContextNetworkAddGroup_Click(sender As System.Object, e As System.EventArgs) Handles ContextNetworkAddGroup.Click
        Call AddGroup()
    End Sub

    Private Sub GroupAddSwitch_Click(sender As System.Object, e As System.EventArgs) Handles GroupAddSwitch.Click
        Call AddSwitch()
    End Sub

    Private Sub ContextGroupAddSwitch_Click(sender As System.Object, e As System.EventArgs) Handles ContextGroupAddSwitch.Click
        Call AddSwitch()
    End Sub

    Private Sub SaveToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles SaveToolStripMenuItem.Click
        Call SaveFile()
    End Sub

    Private Sub ContextGroupTools_Click(sender As System.Object, e As System.EventArgs) Handles ContextGroupTools.Click

    End Sub

    Private Sub ContextGroupAddGroup_Click(sender As System.Object, e As System.EventArgs) Handles ContextGroupAddGroup.Click
        Call AddGroup()
    End Sub

    Private Sub GroupAddGroup_Click(sender As System.Object, e As System.EventArgs) Handles GroupAddGroup.Click
        Call AddGroup()
    End Sub

    Private Sub ConsoleToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles ConsoleToolStripMenuItem.Click
        Dim Switch As PurpleKnife.Library.pkSwitch = DirectCast(GetSelectedElement(), PurpleKnife.Library.pkSwitch)
        Call OpenConsole(Switch)
    End Sub

    Private Sub ConsoleToolStripMenuItem1_Click(sender As System.Object, e As System.EventArgs) Handles ConsoleToolStripMenuItem1.Click
        Dim Switch As PurpleKnife.Library.pkSwitch = DirectCast(GetSelectedElement, PurpleKnife.Library.pkSwitch)
        Call OpenConsole(Switch)
    End Sub

    Private Function GetSelectedElement() As PurpleKnife.Library.pkTreeElement
        Dim NodeInfo As BrowserNodeInfo = DirectCast(trvNetwork.SelectedNode.Tag, BrowserNodeInfo)
        Dim Element As PurpleKnife.Library.pkTreeElement = NodeInfo.ListElement
        Return Element
    End Function

    Private Sub OpenToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles OpenToolStripMenuItem.Click
        Dim loadFileDlg As New OpenFileDialog
        Dim loadFileRes As DialogResult
        With loadFileDlg
            .AddExtension = True
            .AutoUpgradeEnabled = True
            .CheckFileExists = True
            .CheckPathExists = True
            .DefaultExt = ".cfg"
            .DereferenceLinks = True
            .Filter = "Configuration Files|*.cfg|All Files|*.*"
            .Multiselect = False
            .RestoreDirectory = True
            .Title = "Open Config File [PurpleKnife]"
            .ValidateNames = True
            loadFileRes = .ShowDialog(Me)
        End With
        If loadFileRes = Windows.Forms.DialogResult.OK Then
            LoadFile(loadFileDlg.FileName)
            BuildTreeView()
        End If
    End Sub

    Private Sub SaveAsToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles SaveAsToolStripMenuItem.Click
        Call SaveFile()
    End Sub

    Private Sub ContextMenuSwitchProperties_Click(sender As System.Object, e As System.EventArgs) Handles ContextMenuSwitchProperties.Click
        Dim Switch As PurpleKnife.Library.pkSwitch = DirectCast(GetSelectedElement(), PurpleKnife.Library.pkSwitch)
        Call ShowSwitchProperties(Switch)
    End Sub

    Private Sub MenuSwitchProperties_Click(sender As System.Object, e As System.EventArgs) Handles MenuSwitchProperties.Click
        Dim Switch As PurpleKnife.Library.pkSwitch = DirectCast(GetSelectedElement(), PurpleKnife.Library.pkSwitch)
        Call ShowSwitchProperties(Switch)
    End Sub

    Private Sub ShowSwitchProperties(Switch As PurpleKnife.Library.pkSwitch)
        With frmSwitchProperties
            .txtDisplayName.Text = Switch.DisplayName
            .txtIPAddressHostname.Text = Switch.SSHHost
            .txtPort.Text = Switch.SSHPort.ToString
            .txtUserName.Text = Switch.SSHUserName
            .txtSSHHostKeyFingerprint.Text = Switch.SSHKeyFingerprint
            .txtPassword.Text = Switch.SSHPassword
            ' create parent group dropdown list
            .cmbParentGroup.Items.Clear()
            ' add root group to parent list
            .cmbParentGroup.Items.Add(Network.DisplayName)
            ' add all groups to parent list
            Dim GroupList As List(Of PurpleKnife.Library.pkGroup) = GetSubGroups(Network)
            ' parent object of switch must be parent group
            AddGroupList(GroupList, .cmbParentGroup, trvNetwork.SelectedNode.Parent.Text)
            ' hand switch object to properties form
            .Switch = Switch
            .Text = "Properties [" & Switch.DisplayName & "] - PurpleKnife"
        End With
        ShowAsDialog(frmSwitchProperties)
        ' rebuild treeview
        BuildTreeView(Switch.DisplayName)
    End Sub

    Private Sub MenuGroup_Click(sender As System.Object, e As System.EventArgs) Handles MenuGroup.Click

    End Sub
End Class